/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Data.Services.Client;
using System.Reflection;

namespace Xceed.Silverlight.Data
{
  public sealed class WcfDataServicesDataSourceProvider : WcfDataServicesDataSourceProviderBase
  {
    #region Constructors

    public WcfDataServicesDataSourceProvider( DataServiceQuery dataServiceQuery )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, null, true );
    }

    public WcfDataServicesDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      bool canCount )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, null, canCount );
    }

    public WcfDataServicesDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, dataServiceContext, true );
    }

    public WcfDataServicesDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool canCount )
    {
      if( dataServiceQuery == null )
        throw new ArgumentNullException( "dataServiceQuery" );

      this.Initialize( dataServiceQuery, dataServiceContext, canCount );
    }

    #endregion

    #region CanCount Property

    public override bool CanCount
    {
      get
      {
        return m_dataSourceProvider.CanCount;
      }
    }

    #endregion

    #region ElementType Protected Property

    protected internal override Type ElementType
    {
      get
      {
        return m_dataSourceProvider.ElementType;
      }
    }

    #endregion

    protected override void Initialize()
    {
      m_dataSourceProvider.Init();

      base.Initialize();
    }

    protected override IAsyncResult BeginExecuteCountQuery(
      CountQueryParameters queryParameters,
      AsyncCallback callback,
      object state )
    {
      return m_dataSourceProvider.BeginExecuteCountQueryCore( queryParameters, callback, state );
    }

    protected override CountQueryResult EndExecuteCountQuery( IAsyncResult asyncResult )
    {
      return m_dataSourceProvider.EndExecuteCountQueryCore( asyncResult );
    }

    protected override IAsyncResult BeginExecuteDataQuery(
      DataQueryParameters queryParameters,
      AsyncCallback callback,
      object state )
    {
      return m_dataSourceProvider.BeginExecuteDataQueryCore( queryParameters, callback, state );
    }

    protected override DataQueryResult EndExecuteDataQuery( IAsyncResult asyncResult )
    {
      return m_dataSourceProvider.EndExecuteDataQueryCore( asyncResult );
    }

    private void Initialize(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool forceCount )
    {
      m_dataSourceProvider = WcfDataServicesDataSourceProvider.CreateDataSourceProvider(
                               dataServiceQuery,
                               dataServiceContext,
                               forceCount );
    }

    private static WcfDataServicesDataSourceProviderBase<TElement> CreateDataSourceProvider<TElement>(
      DataServiceQuery<TElement> dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool forceCount )
    {
      // Check if we are allowed to call IncludeTotalCount ourself.
      if( ( forceCount ) && ( !WcfDataServicesDataSourceProvider.IsCountingEnabled( dataServiceQuery ) ) )
      {
        try
        {
          dataServiceQuery = dataServiceQuery.IncludeTotalCount();
        }
        catch( Exception )
        {
          // We were not able to activate counting ourself.
          // Use the DataServiceQuery that was given by the user instead.
        }
      }

      return ( dataServiceContext != null )
               ? new WcfDataServicesDataSourceProvider<TElement>( dataServiceQuery, dataServiceContext )
               : new WcfDataServicesDataSourceProvider<TElement>( dataServiceQuery );
    }

    private static WcfDataServicesDataSourceProviderBase CreateDataSourceProvider(
      DataServiceQuery dataServiceQuery,
      DataServiceContext dataServiceContext,
      bool forceCount )
    {
      Type genericDataServiceQueryType = typeof( DataServiceQuery<> );
      genericDataServiceQueryType = genericDataServiceQueryType.MakeGenericType( dataServiceQuery.ElementType );

      // The current DataSourceProvider works only if the DataServiceQuery is 
      // of type DataServiceQuery<>.
      if( genericDataServiceQueryType.IsAssignableFrom( dataServiceQuery.GetType() ) )
      {
        MethodInfo currentMethodInfo = ( MethodInfo )MethodBase.GetCurrentMethod();

        foreach( MethodInfo methodInfo in currentMethodInfo.ReflectedType.GetMethods( BindingFlags.Static | BindingFlags.NonPublic ) )
        {
          if( ( methodInfo.IsGenericMethod ) && ( methodInfo.Name == currentMethodInfo.Name ) )
          {
            MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod( dataServiceQuery.ElementType );
            object dataSourceProvider = genericMethodInfo.Invoke( null, new object[] { dataServiceQuery, dataServiceContext, forceCount } );

            return ( WcfDataServicesDataSourceProviderBase )dataSourceProvider;
          }
        }
      }

      throw new NotSupportedException();
    }

    private static bool IsCountingEnabled<TElement>( DataServiceQuery<TElement> dataServiceQuery )
    {
      if( dataServiceQuery == null )
        return false;

      Uri uri = dataServiceQuery.RequestUri;

      return ( uri != null )
          && ( uri.Query.ToLowerInvariant().Contains( "$inlinecount=allpages" ) );
    }

    #region Private Fields

    private WcfDataServicesDataSourceProviderBase m_dataSourceProvider; //null

    #endregion
  }
}
